home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / PROGRAM / MSJV7_2B.ARJ / CLIDEMO.C next >
C/C++ Source or Header  |  1992-03-01  |  39KB  |  1,033 lines

  1. /* 
  2.  * clidemo.c - OLE client application sample code
  3.  *
  4.  * Created by Microsoft Corporation.
  5.  * (c) Copyright Microsoft Corp. 1990 - 1992  All Rights Reserved
  6.  *
  7.  */
  8.  
  9.  /***************************************************************************
  10.  * IMPORTANT - README:
  11.  * OLE client applications are windows programs which use the OLE client
  12.  * APIs.  Therefore it is imperative that you understand how these APIs
  13.  * operate. Most importantly it is essential that you keep in mind which
  14.  * procedure calls result in asynchronous states: a state where the operation
  15.  * is not truely complete after a return from the call.
  16.  * 
  17.  * Many functions produce asynchronous states, for example, OleActivate, 
  18.  * OleClose, OleCopyFromLink, OleCreate ... Reference your SDK manual for
  19.  * a complete list.
  20.  *
  21.  * So whenever you call any of these library functions keep in mind that
  22.  * the operation is not necessarily complete once a return is made.
  23.  * These operations require communications with a server application.  With
  24.  * OLE the inter-application communication is done through DDE.  In order
  25.  * for a DDE conversation to complete several DDE messages need to be 
  26.  * sent and recieved by both the server and client OLE DLLs.  So, the 
  27.  * asynchronous operations will not complete until the client application
  28.  * enters a message dipatch loop.  Therefore, it is necessary to enter
  29.  * a dispatch loop and wait for completion.  It is not necessary to block 
  30.  * all other operation; however, it is very important to coordinate the
  31.  * user activity to prevent disastrous re-entry cases.
  32.  *
  33.  * In this application I have written a macro to prevent re-entry
  34.  * problems.  Namely: ANY_OBJECT_BUSY which prevents a user from initiating
  35.  * an action which will result in an asynchronous call if there is an object   
  36.  * already in an asynchronous state.   
  37.  * 
  38.  * The following is brief summary of the three macros:
  39.  *
  40.  * ANY_OBJECT_BUSY: checks to see if any object in the document is busy.
  41.  *              This prevents a new document from being saved to file if there are
  42.  *              objects in asynchronous states.
  43.  *
  44.  * So, the problem is that we have to enter a message dispatch loop in order
  45.  * to let DDE messages get through so that asynchronous operations can finish.
  46.  * And while we are in the message dispatch loops (WaitForObject or WaitForAllObjects)
  47.  * we have to prevent the user from doing things that can't be done when an
  48.  * object(s) is busy.  Yes, it is confusing , but, the end result is a super
  49.  * cool application that can have linked and embbeded objects! 
  50.  ***************************************************************************/
  51.  
  52. //*** INCLUDES ***
  53.  
  54. #include <windows.h>                   //* WINDOWS 
  55. #include <ole.h>                       //* OLE structs and defines
  56. #include <shellapi.h>                  //* Shell, drag and drop headers 
  57.  
  58. #include "demorc.h"                    //* header for resource file
  59. #include "global.h"                    //* global app variables
  60. #include "clidemo.h"                   //* app includes:
  61. #include "register.h"
  62. #include "stream.h"
  63. #include "object.h"
  64. #include "dialog.h"
  65. #include "utility.h"
  66.  
  67. //*** VARIABLES ***
  68.  
  69. //** Global
  70. BOOL              fRetry = FALSE;
  71. HWND              hwndFrame;           //* main window
  72. HANDLE            hAccTable;           //* accelerator table
  73. HANDLE            hInst;               //* Instance Handle
  74. char              szFrameClass[] = "CliDemo";//* main window class name
  75. char              szItemClass[]  = "ItemClass";//* item window class name
  76. char              szAppName[CBMESSAGEMAX];//* Application name
  77. int               iObjects = 0;        //* object count
  78. int               iObjectNumber = 0;   //* object number for object name
  79. char              szFileName[CBPATHMAX];
  80.                                        //* ClipBoard formats:
  81. OLECLIPFORMAT     vcfLink;             //* "ObjectLink" 
  82. OLECLIPFORMAT     vcfNative;           //* "Native"
  83. OLECLIPFORMAT     vcfOwnerLink;        //* "OwnerLink"
  84.  
  85. extern int        giXppli;             //* pixels per logical inch along width
  86. extern int        giYppli;             //* pixels per logical inch along height 
  87.  
  88. /***************************************************************************
  89.  * WinMain() - Main Windows routine
  90.  ***************************************************************************/
  91.  
  92. int PASCAL WinMain(                    //* ENTRY:
  93.    HANDLE         hInstance,           //* standard windows entry
  94.    HANDLE         hPrevInstance, 
  95.    LPSTR          lpCmdLine, 
  96.    int            nCmdShow
  97. ){
  98.     hInst = hInstance;
  99.  
  100.     if (!hPrevInstance)
  101.         if (!InitApplication(hInst))   //* register window classes
  102.             return FALSE;
  103.  
  104.  
  105.     if (!InitInstance(hInst))          //* create window instance
  106.         return FALSE;
  107.  
  108.     OfnInit(hInst);                    //* setup to use <commdlg.dll>
  109.  
  110.                                        //* register clipboard formats
  111.                                        //* used for OLE
  112.     vcfLink      = RegisterClipboardFormat("ObjectLink");
  113.     vcfNative    = RegisterClipboardFormat("Native");
  114.     vcfOwnerLink = RegisterClipboardFormat("OwnerLink");
  115.     
  116.  
  117.     ShowWindow(hwndFrame, nCmdShow);
  118.     UpdateWindow(hwndFrame);
  119.     ProcessCmdLine(lpCmdLine);
  120.  
  121.     while (ProcessMessage(hwndFrame, hAccTable)) ;
  122.  
  123.     return FALSE;
  124. }
  125.  
  126. /***************************************************************************
  127.  * InitApplication() 
  128.  *
  129.  * registers the window classes used by the application.
  130.  *
  131.  * Returns BOOL:      - TRUE if successful.
  132.  ***************************************************************************/
  133.  
  134. static BOOL InitApplication(           //* ENTRY:
  135.    HANDLE         hInst                //* instance handle
  136. ){                                     //* LOCAL:
  137.    WNDCLASS       wc;                  //* temp wind-class structure
  138.  
  139.    wc.style          = NULL;
  140.    wc.lpfnWndProc    = FrameWndProc;
  141.    wc.cbClsExtra     = 0;
  142.    wc.cbWndExtra     = 0;
  143.    wc.hInstance      = hInst;
  144.    wc.hIcon          = LoadIcon(hInst, MAKEINTRESOURCE(ID_APPLICATION));
  145.    wc.hCursor        = LoadCursor(NULL, IDC_ARROW);
  146.    wc.hbrBackground  = COLOR_APPWORKSPACE + 1;
  147.    wc.lpszMenuName   = MAKEINTRESOURCE(ID_APPLICATION);
  148.    wc.lpszClassName  = szFrameClass;
  149.  
  150.    if (!RegisterClass(&wc))
  151.       return FALSE;
  152.                                        //* application item class
  153.    wc.style          = CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW;
  154.    wc.lpfnWndProc    = ItemWndProc;
  155.    wc.hIcon          = NULL;
  156.    wc.cbWndExtra     = sizeof(APPITEMPTR);
  157.    wc.lpszMenuName   = NULL;
  158.    wc.lpszClassName  = szItemClass;
  159.  
  160.    if (!RegisterClass(&wc))
  161.       return FALSE;
  162.  
  163.    return TRUE;
  164.  
  165. }
  166.  
  167. /***************************************************************************
  168.  * InitInstance()
  169.  *
  170.  * create the main application window.
  171.  *
  172.  * Returns BOOL:      - TRUE if successful else FALSE.
  173.  ***************************************************************************/
  174.  
  175. static BOOL InitInstance(              //* ENTRY:
  176.    HANDLE         hInst                //* instance handel
  177. ){
  178.    HDC  hDC;
  179.    
  180.    hAccTable = LoadAccelerators(hInst, MAKEINTRESOURCE(ID_APPLICATION));
  181.  
  182.    if (!(hwndFrame = 
  183.       CreateWindow(
  184.          szFrameClass, "",
  185.          WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
  186.          CW_USEDEFAULT, CW_USEDEFAULT,   
  187.          CW_USEDEFAULT, CW_USEDEFAULT,   
  188.          NULL, 
  189.          NULL, 
  190.          hInst, 
  191.          NULL
  192.       )))
  193.       return FALSE;                    //* ERROR return
  194.  
  195.    LoadString(hInst, IDS_APPNAME, szAppName, CBMESSAGEMAX);
  196.    DragAcceptFiles(hwndFrame, TRUE);   //* allow dragged and dropped files
  197.  
  198.    // Initialize global variables with LOGPIXELSX and LOGPIXELSY
  199.         
  200.    hDC    = GetDC (NULL);       // Get the hDC of the desktop window
  201.    giXppli = GetDeviceCaps (hDC, LOGPIXELSX);
  202.    giYppli = GetDeviceCaps (hDC, LOGPIXELSY);
  203.    ReleaseDC (NULL, hDC);
  204.  
  205.        
  206.    return TRUE;                        //* SUCCESS return
  207.  
  208. }
  209.  
  210. /***************************************************************************
  211.  *  ProcessCmdLine()
  212.  *
  213.  *  process command line getting any command arguments.  
  214.  ***************************************************************************/
  215.  
  216. void ProcessCmdLine(                   //* ENTRY:
  217.    LPSTR          lpCmdLine            //* command line argument
  218. ){                                     //* LOCAL:
  219.    LPSTR          lpstrExt = lpCmdLine;//* pointer to file extension
  220.    OFSTRUCT       ofs;                      
  221.  
  222.    if (*lpCmdLine)                     
  223.    {                                   //* look for file extension
  224.       while (*lpstrExt && *lpstrExt != '.') 
  225.          lpstrExt = AnsiNext(lpstrExt);
  226.  
  227.       lstrcpy(szFileName, lpCmdLine);
  228.       if (!(*lpstrExt))                //* append default extension
  229.       {
  230.          lstrcat(szFileName,".");
  231.          lstrcat(szFileName,szDefExtension);
  232.       }
  233.                                        //* get the files fully 
  234.       OpenFile(szFileName, &ofs, OF_PARSE);//* qualified name
  235.       lstrcpy(szFileName, ofs.szPathName);
  236.    }
  237.    else 
  238.       *szFileName = NULL;
  239.                                        //* pass filename to main winproc
  240.    SendMessage(hwndFrame,WM_INIT,NULL,(long)NULL);
  241.  
  242. }
  243.  
  244.  
  245. /***************************************************************************
  246.  *  FrameWndProc()
  247.  *
  248.  *  Message handler for the application frame window.
  249.  *
  250.  *  Returns long - Variable, depends on message.
  251.  ***************************************************************************/
  252.  
  253. long FAR PASCAL FrameWndProc(          //* ENTRY:
  254.    HWND           hwnd,                //* standard wind-proc parameters 
  255.    unsigned       msg, 
  256.    WORD           wParam, 
  257.    LONG           lParam
  258. ){                                     //* LOCAL:
  259.                                        //* ^ Document file name
  260.    static LHCLIENTDOC   lhcDoc;        //* Document Handle
  261.    static LPOLECLIENT   lpClient;      //* pointer to client 
  262.    static LPAPPSTREAM   lpStream;      //* pointer to stream vtbl
  263.    APPITEMPTR           pItem;         //* application item pointer
  264.  
  265.    switch (msg) 
  266.    {     
  267.       case WM_INIT:                    //* user defined message
  268.          if (!InitAsOleClient(hInst, hwnd, szFileName, &lhcDoc, &lpClient, &lpStream))
  269.             DestroyWindow(hwnd);
  270.          break;
  271.                                        //* the following three messages are
  272.                                        //* used to avoid problems with OLE
  273.                                        //* see the comment in object.h
  274.       case WM_DELETE:                  //* user defined message
  275.          pItem = (APPITEMPTR) wParam;  //* delete object
  276.          WaitForObject(pItem);
  277.          ObjDelete(pItem,DELETE);
  278.          if (lParam)
  279.             cOleWait--;
  280.          break;
  281.  
  282.       case WM_ERROR:                   //* user defined message
  283.          ErrorMessage(wParam);         //* display error message
  284.          break;
  285.  
  286.       case WM_RETRY:                   //* user defined message
  287.          RetryMessage((APPITEMPTR)wParam, RD_RETRY | RD_CANCEL);
  288.          break;
  289.  
  290.       case WM_INITMENU:
  291.          UpdateMenu((HMENU)wParam);
  292.          break;
  293.  
  294.       case WM_COMMAND:
  295.          pItem = GetTopItem();
  296.          switch (wParam) 
  297.          {
  298.             case IDM_NEW:
  299.                ANY_OBJECT_BUSY;
  300.                NewFile(szFileName,&lhcDoc,lpStream);
  301.                break;
  302.  
  303.             case IDM_OPEN:
  304.                ANY_OBJECT_BUSY;
  305.                MyOpenFile(szFileName,&lhcDoc,lpClient,lpStream);
  306.                break;
  307.  
  308.             case IDM_SAVE:
  309.                ANY_OBJECT_BUSY;
  310.                SaveFile(szFileName,lhcDoc,lpStream);
  311.                break;
  312.  
  313.             case IDM_SAVEAS:
  314.                ANY_OBJECT_BUSY;
  315.                SaveasFile(szFileName,lhcDoc,lpStream);
  316.                break;
  317.  
  318.             case IDM_ABOUT:
  319.                AboutBox();
  320.                break;
  321.  
  322.             case IDM_INSERT:
  323.                ANY_OBJECT_BUSY;
  324.                ObjInsert(lhcDoc, lpClient);
  325.                break;
  326.  
  327.             case IDM_INSERTFILE:
  328.                ANY_OBJECT_BUSY;
  329.                ObjCreateFromTemplate(lhcDoc,lpClient);
  330.                break;
  331.  
  332.             case IDM_PASTE:
  333.             case IDM_PASTELINK:
  334.                ANY_OBJECT_BUSY;
  335.                ObjPaste(wParam == IDM_PASTE,lhcDoc,lpClient); 
  336.                break;
  337.  
  338.             case IDM_LINKS:
  339.                ANY_OBJECT_BUSY;
  340.                pItem = GetTopItem();
  341.                LinkProperties();
  342.                break;
  343.             
  344.             case IDM_EXIT:
  345.                ANY_OBJECT_BUSY;
  346.                SendMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0L);
  347.                break;
  348.  
  349.             case IDM_COPY:
  350.             case IDM_CUT:
  351.                ANY_OBJECT_BUSY;
  352.              
  353.                if (!ObjCopy(pItem))
  354.                {
  355.                   ErrorMessage((wParam == IDM_CUT) ?
  356.                      E_CLIPBOARD_CUT_FAILED : E_CLIPBOARD_COPY_FAILED);
  357.                   break;
  358.                }   
  359.  
  360.                if (wParam == IDM_COPY)
  361.                   break;
  362.  
  363.             case IDM_CLEAR:            //* CUT falls through to clear
  364.                ANY_OBJECT_BUSY;
  365.                ClearItem(pItem);
  366.                break;
  367.  
  368.             case IDM_CLEARALL:
  369.                ANY_OBJECT_BUSY;
  370.                ClearAll(lhcDoc,DELETE);
  371.                Dirty(DOC_DIRTY);
  372.                break;
  373.  
  374.             default:
  375.                if( (wParam >= IDM_VERBMIN) && (wParam <= IDM_VERBMAX) )
  376.                {    
  377.                   ANY_OBJECT_BUSY;
  378.                   ExecuteVerb(wParam - IDM_VERBMIN,pItem);
  379.                   break;
  380.                }
  381.                return DefWindowProc(hwnd, msg, wParam, lParam);
  382.          }
  383.          break;
  384.  
  385.       case WM_DROPFILES:
  386.          ANY_OBJECT_BUSY;
  387.          ObjCreateWrap(wParam, lhcDoc, lpClient);
  388.          break;
  389.  
  390.       case WM_CLOSE:
  391.          ANY_OBJECT_BUSY;   
  392.          if (!SaveAsNeeded(szFileName, lhcDoc, lpStream))
  393.             break;
  394.          DeregDoc(lhcDoc);
  395.          DestroyWindow(hwnd);
  396.          break;
  397.  
  398.       case WM_DESTROY:
  399.          EndStream(lpStream);
  400.          EndClient(lpClient);
  401.          PostQuitMessage(0);
  402.          break;
  403.                                        
  404.       case WM_QUERYENDSESSION:         //* don't let windows terminate 
  405.          return (QueryEndSession(szFileName,lhcDoc, lpStream));
  406.  
  407.       default:
  408.          return DefWindowProc(hwnd, msg, wParam, lParam);
  409.    } 
  410.    return 0L;
  411.  
  412. }
  413.  
  414. /***************************************************************************
  415.  * InitAsOleClient()
  416.  *
  417.  * Initiates the creation of stream and client vtbls.  These vtbls are very
  418.  * important for the proper operation of this application.  The stream vtbl
  419.  * lets the OLE librarys know where the location of the stream I/O routines
  420.  * reside.  The stream routines are used by OleLoadFromStream and the like.
  421.  * The client vtbl is used to hold the pointer to the CallBack function.
  422.  * IMPORTANT: both the client and the stream structures have pointers to 
  423.  * vtbls which have the pointers to the functions.  Therefore, it is 
  424.  * necessary to allocate space for the vtbl and the client structure
  425.  * which has the pointer to the vtbl.  
  426.  **************************************************************************/
  427.  
  428. static BOOL InitAsOleClient(           //* ENTRY:
  429.    HANDLE         hInstance,           //* applicaion instance handle
  430.    HWND           hwnd,                //* main window handle
  431.    PSTR           pFileName,           //* document file name
  432.    LHCLIENTDOC    *lhcDoc,             //* pointer to document Handle
  433.    LPOLECLIENT    *lpClient,           //* pointer to client pointer 
  434.    LPAPPSTREAM    *lpStream            //* pointer to APPSTREAM pointer
  435. ){
  436.                                        //* initiate client vtbl creation
  437.    if (!(*lpClient = InitClient(hInstance)))
  438.    {
  439.       SendMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0L);
  440.       return FALSE;                    //* ERROR return
  441.    }
  442.                                        //* initiate stream vtbl creation
  443.    if (!(*lpStream = InitStream(hInstance)))
  444.    {
  445.       SendMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0L);
  446.       return FALSE;                    //* ERROR return
  447.    }
  448.  
  449.    if (*pFileName && RegDoc(pFileName,lhcDoc)
  450.        && LoadFile(pFileName,*lhcDoc,*lpClient,*lpStream)) 
  451.    {  
  452.       SetTitle(pFileName); 
  453.       return TRUE;                     //* SUCCESS return
  454.    }
  455.  
  456.    NewFile(pFileName, lhcDoc, *lpStream);
  457.    return TRUE;                        //* SUCCESS return
  458.  
  459. }                                      //* SUCCESS return
  460.  
  461. /****************************************************************************
  462.  *  InitClient()
  463.  *
  464.  *  Initialize the OLE client structure, create and fill the OLECLIENTVTBL 
  465.  *  structure.
  466.  *
  467.  *  Returns LPOLECLIENT - if successful a pointer to a client structure
  468.  *                        , otherwise NULL.
  469.  ***************************************************************************/
  470.  
  471. static LPOLECLIENT InitClient(         //* ENTRY:
  472.    HANDLE hInstance                    //* application instance handle
  473. ){                                     //* LOCAL:
  474.    LPOLECLIENT lpClient=NULL;          //* pointer to client struct
  475.                                        //* Allocate vtbls
  476.    if (!(lpClient = (LPOLECLIENT)GlobalLock(
  477.          GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(OLECLIENT))
  478.       )))
  479.       goto Error;                      //* ERROR jump        
  480.  
  481.    if (!(lpClient->lpvtbl = (LPOLECLIENTVTBL)GlobalLock(
  482.             GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(OLECLIENTVTBL))
  483.       )))
  484.       goto Error;                      //* ERROR jump
  485.                                        //* set the CALLBACK function
  486.                                        //* pointer
  487.    lpClient->lpvtbl->CallBack  = MakeProcInstance(CallBack, hInst);
  488.  
  489.    return lpClient;                    //* SUCCESS return
  490.    
  491. Error:                                 //* ERROR Tag
  492.    
  493.    ErrorMessage(E_FAILED_TO_ALLOC);
  494.    EndClient(lpClient);                //* free any allocated space
  495.  
  496.    return NULL;                        //* ERROR return
  497.   
  498. }
  499.     
  500. /****************************************************************************
  501.  *  InitStream()
  502.  *
  503.  *  Create and fill the STREAMVTBL. Create a stream structure and initialize
  504.  *  pointer to stream vtbl.
  505.  *
  506.  *  Returns LPAPPSTREAM - if successful a pointer to a stream structure
  507.  *                        , otherwise NULL .     
  508.  ***************************************************************************/
  509.  
  510. static LPAPPSTREAM InitStream(         //* ENTRY:
  511.    HANDLE hInstance                    //* handle to application instance
  512. ){                                     //* LOCAL:
  513.    LPAPPSTREAM lpStream = NULL;        //* pointer to stream structure
  514.  
  515.    if (!(lpStream = (LPAPPSTREAM)GlobalLock(
  516.          GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(APPSTREAM))
  517.       )))
  518.       goto Error;                      //* ERROR jump
  519.  
  520.    if (!(lpStream->olestream.lpstbl = (LPOLESTREAMVTBL)GlobalLock(
  521.          GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(OLESTREAMVTBL))
  522.       )))
  523.       goto Error;                      //* ERROR jump
  524.  
  525.                                        //* set stream func. pointers
  526.    lpStream->olestream.lpstbl->Get = (DWORD (FAR PASCAL *)(LPOLESTREAM, LPSTR, DWORD))
  527.                       MakeProcInstance((FARPROC)ReadStream, hInst);
  528.    lpStream->olestream.lpstbl->Put = (DWORD (FAR PASCAL *)(LPOLESTREAM, LPSTR, DWORD))
  529.                       MakeProcInstance((FARPROC)WriteStream, hInst);
  530.  
  531.    return lpStream;                    //* SUCCESS return
  532.  
  533. Error:                                 //* ERROR Tag
  534.  
  535.    ErrorMessage(E_FAILED_TO_ALLOC);
  536.    EndStream(lpStream);
  537.  
  538.    return NULL;                        //* ERROR return
  539.  
  540. }
  541.  
  542. /***************************************************************************
  543.  *  UpdateMenu()
  544.  *
  545.  *  Enabling or disable menuitems based upon program state.
  546.  ***************************************************************************/
  547.  
  548. static void UpdateMenu(                //* ENTRY:
  549.    HMENU       hMenu                   //* menu handle to updated
  550. ){                                     //* LOCAL:
  551.    int         mf;                     //* generic menu flag 
  552.    APPITEMPTR  paItem;                 //* app item pointer
  553.    HMENU       hSub;
  554.                                        //* there must be at least on object
  555.                                        //* for the following to be enabled
  556.  
  557.    paItem = GetTopItem() ; 
  558.  
  559.    mf = (paItem ? MF_ENABLED : MF_GRAYED);
  560.    EnableMenuItem(hMenu, IDM_CUT, mf); //* i.e. Cut,Copy,Clear,Clearall...
  561.    EnableMenuItem(hMenu, IDM_COPY, mf);
  562.    EnableMenuItem(hMenu, IDM_CLEAR, mf);
  563.    EnableMenuItem(hMenu, IDM_CLEARALL, mf);
  564.                                        //* enable links option only if there
  565.                                        //* is at least one linked object
  566.    EnableMenuItem(hMenu, IDM_LINKS, MF_GRAYED);
  567.    for (; paItem; paItem = GetNextItem(paItem))
  568.    {
  569.       if (paItem->otObject == OT_LINK)
  570.       {   
  571.          EnableMenuItem(hMenu, IDM_LINKS, MF_ENABLED);
  572.          break;
  573.       }
  574.    }
  575.  
  576.    if (hSub = GetSubMenu(hMenu,POS_EDITMENU))
  577.       UpdateObjectMenuItem(hSub);
  578.  
  579.    if (OleQueryCreateFromClip(STDFILEEDITING, olerender_draw, 0) == OLE_OK)
  580.       EnableMenuItem(hMenu, IDM_PASTE, MF_ENABLED);
  581.    else if (OleQueryCreateFromClip(STATICP, olerender_draw, 0) == OLE_OK)
  582.       EnableMenuItem(hMenu, IDM_PASTE, MF_ENABLED);
  583.    else  
  584.       EnableMenuItem(hMenu, IDM_PASTE, MF_GRAYED);
  585.  
  586.    if (OleQueryLinkFromClip(STDFILEEDITING, olerender_draw, 0) == OLE_OK)
  587.       EnableMenuItem(hMenu, IDM_PASTELINK, MF_ENABLED);
  588.    else  
  589.       EnableMenuItem(hMenu, IDM_PASTELINK, MF_GRAYED);
  590.  
  591. }
  592.  
  593. /***************************************************************************
  594.  *  NewFile()
  595.  *
  596.  *  Save the present document and open a new blank one. 
  597.  ***************************************************************************/
  598.  
  599. static void NewFile(                   //* ENTRY:
  600.    PSTR           pFileName,           //* open file name
  601.    LHCLIENTDOC    *lhcptrDoc,          //* pointer to client doc. handle
  602.    LPAPPSTREAM    lpStream             //* pointer to stream structure
  603. ){                                     //* LOCAL:
  604.    static char  szUntitled[CBMESSAGEMAX] = "";//* "(Untitled)" string 
  605.    LHCLIENTDOC lhcDocNew;              //* handle for new doc.
  606.  
  607.    if (!(*szUntitled))
  608.       LoadString(hInst, IDS_UNTITLED, (LPSTR)szUntitled, CBMESSAGEMAX);
  609.  
  610.    if (SaveAsNeeded(pFileName, *lhcptrDoc, lpStream))
  611.    {                                   //* try to register new document
  612.       if (!RegDoc(szUntitled, &lhcDocNew)) 
  613.          return;                       //* before deregistring the old one
  614.       DeregDoc(*lhcptrDoc);            
  615.       *lhcptrDoc = lhcDocNew;
  616.       Dirty(DOC_CLEAN);                //* new document is clean
  617.       lstrcpy(pFileName,szUntitled);
  618.       SetTitle(pFileName);
  619.       iObjectNumber = 0;
  620.    }
  621.  
  622. }
  623.  
  624. /***************************************************************************
  625.  *  MyOpenFile()
  626.  *
  627.  *  Open a file and load it.  Notice that the new file is loaded before
  628.  *  the old is removed.  This is done to assure a succesful file load
  629.  *  before removing an existing document. 
  630.  ***************************************************************************/
  631.  
  632. static void MyOpenFile(                //* ENTRY:
  633.    PSTR           pFileName,           //* open file name
  634.    LHCLIENTDOC    *lhcptrDoc,          //* pointer to document handle
  635.    LPOLECLIENT    lpClient,            //* pointer to client structure
  636.    LPAPPSTREAM    lpStream             //* pointer to stream structure
  637. ){                                     //* LOCAL:
  638.    char           szNewFile[CBPATHMAX];//* new file name buffer
  639.    LHCLIENTDOC    lhcDocNew;           //* handle of new document
  640.    APPITEMPTR     pItem;               //* hold top item
  641.  
  642.    if (SaveAsNeeded(pFileName, *lhcptrDoc, lpStream))
  643.    {
  644.       *szNewFile = NULL;
  645.  
  646.       if (!OfnGetName(hwndFrame, szNewFile, IDM_OPEN)) 
  647.          return;                       //* ERROR return
  648.       
  649.       if (!RegDoc(szNewFile,&lhcDocNew)) 
  650.          return;                       //* ERROR return
  651.       
  652.       pItem = GetTopItem();
  653.       ShowDoc(*lhcptrDoc,0);           //* make old doc objects hidden.
  654.                                        //* try to load the new file before
  655.       if (!LoadFile(szNewFile, lhcDocNew, lpClient, lpStream))
  656.       {                                //* before removing the old.
  657.          DeregDoc(lhcDocNew);          //* restore old document if new
  658.          SetTopItem(pItem);            //* file did not load
  659.          ShowDoc(*lhcptrDoc,1);
  660.          return;                       //* ERROR return
  661.       }
  662.       
  663.       DeregDoc(*lhcptrDoc);            //* deregister old document
  664.       *lhcptrDoc = lhcDocNew;
  665.       lstrcpy(pFileName,szNewFile);
  666.       SetTitle(pFileName);             //* set new title
  667.       Dirty(DOC_CLEAN);   
  668.    }
  669.  
  670. }                                      //* SUCCESS return
  671.  
  672. /***************************************************************************
  673.  *  SaveasFile()
  674.  *
  675.  * Prompt the user for a new file name.  Write the document to the new
  676.  * filename.
  677.  ***************************************************************************/
  678.  
  679. static void SaveasFile(                //* ENTRY:
  680.    PSTR           pFileName,           //* old filename
  681.    LHCLIENTDOC    lhcDoc,              //* document handle
  682.    LPAPPSTREAM    lpStream             //* pointer to stream structure
  683. ){
  684.    char           szNewFile[CBPATHMAX];//* new file name
  685.  
  686.    *szNewFile = NULL;                  //* prompt user for new file name
  687.    if (!OfnGetName(hwndFrame, szNewFile, IDM_SAVEAS)) 
  688.       return;                          //* ERROR return
  689.                                        //* rename document
  690.    if (!SaveFile(szNewFile, lhcDoc, lpStream))
  691.       return;
  692.  
  693.    if (Error(OleRenameClientDoc(lhcDoc, szNewFile)))
  694.    {
  695.       ErrorMessage(W_FAILED_TO_NOTIFY);
  696.       return;                          //* ERROR return
  697.    }   
  698.  
  699.    lstrcpy(pFileName,szNewFile);
  700.    SetTitle(pFileName);
  701.  
  702. }                                      //* SUCCESS return
  703.  
  704. /***************************************************************************
  705.  *  SaveFile()
  706.  *
  707.  * Save a compound document file.  If the file is untitled, ask the user
  708.  * for a name and save the document to that file. 
  709.  ***************************************************************************/
  710.  
  711. static BOOL SaveFile(                  //* ENTRY:
  712.    PSTR           pFileName,           //* file to save document to
  713.    LHCLIENTDOC    lhcDoc,              //* OLE document handle
  714.    LPAPPSTREAM    lpStream             //* pointer to app. stream struct
  715. ){                                     //* LOCAL:
  716.    char           szNewFile[CBPATHMAX];//* New file name strings
  717.    char           szOemFileName[2*CBPATHMAX]; 
  718.    static char    szUntitled[CBMESSAGEMAX] = "";
  719.    HANDLE         fh;                  //* file handle
  720.    
  721.    *szNewFile = NULL;
  722.    if (!(*szUntitled))
  723.       LoadString(hInst, IDS_UNTITLED, (LPSTR)szUntitled, CBMESSAGEMAX);
  724.                     
  725.    if (!lstrcmp(szUntitled, pFileName))//* get filename for the untitled case
  726.    {
  727.       if (!OfnGetName(hwndFrame, szNewFile, IDM_SAVEAS))
  728.          return FALSE;                 //* CANCEL return    
  729.       lstrcpy(pFileName,szNewFile);
  730.       SetTitle(pFileName);
  731.    }
  732.  
  733.    AnsiToOem(pFileName, szOemFileName);
  734.    if ((fh =_lcreat((LPSTR)szOemFileName, 0)) <= 0) 
  735.    {
  736.       ErrorMessage(E_INVALID_FILENAME);
  737.       return FALSE;                    //* ERROR return
  738.    }
  739.  
  740.    lpStream->fh = fh;
  741.                                        //* save file on disk
  742.    if (!WriteToFile(lpStream)) 
  743.    {
  744.       _lclose(fh);
  745.       ErrorMessage(E_FAILED_TO_SAVE_FILE);
  746.       return FALSE;                    //* ERROR return
  747.    }
  748.    _lclose(fh);
  749.  
  750.    if (Error(OleSavedClientDoc(lhcDoc)))
  751.    {
  752.       ErrorMessage(W_FAILED_TO_NOTIFY);
  753.       return FALSE;                    //* ERROR return
  754.    }
  755.  
  756.    Dirty(DOC_CLEAN);
  757.    return TRUE;                        //* SUCCESS return
  758.  
  759.  
  760. /***************************************************************************
  761.  *  LoadFile()
  762.  *
  763.  *  Load a document file from disk. 
  764.  ***************************************************************************/
  765.  
  766. static BOOL LoadFile(                  //* ENTRY:
  767.    PSTR           pFileName,           //* file name
  768.    LHCLIENTDOC    lhcDoc,              //* document handle
  769.    LPOLECLIENT    lpClient,            //* pointer to client structure
  770.    LPAPPSTREAM    lpStream             //* pointer to stream structure
  771. ){                                     //* LOCAL:
  772.                                        //* OEM file name
  773.    char           szOemFileName[2*CBPATHMAX];      
  774.    HANDLE         fh;                  //* file handle
  775.    int iObjectNumberHold;              //* hold object number
  776.  
  777.    AnsiToOem(pFileName, szOemFileName);
  778.    if ((fh = _lopen(szOemFileName, OF_READ | OF_SHARE_DENY_WRITE)) == -1) 
  779.    {
  780.       ErrorMessage(E_FAILED_TO_READ_FILE);
  781.       return FALSE;                    //* ERROR return
  782.    }
  783.  
  784.    lpStream->fh = fh;
  785.  
  786.    iObjectNumberHold = iObjectNumber;  //* save object number so it can 
  787.    iObjectNumber     = 0;              //* be restored if read from file
  788.                                        //* fails
  789.    if (!ReadFromFile(lpStream, lhcDoc, lpClient)) 
  790.    {
  791.       _lclose(fh);
  792.       ErrorMessage(E_FAILED_TO_READ_FILE);
  793.       iObjectNumber = iObjectNumberHold;
  794.       return FALSE;                    //* ERROR return
  795.    }
  796.    return TRUE;                        //* SUCCESS return
  797.  
  798.  
  799. /***************************************************************************
  800.  *  RegDoc()
  801.  *
  802.  * Register the client document with the OLE library.
  803.  **************************************************************************/
  804.  
  805. static BOOL RegDoc(                    //* ENTRY:
  806.    PSTR           pFileName,           //* file name
  807.    LHCLIENTDOC    *lhcptrDoc           //* pointer to client document handle
  808. ){
  809.  
  810.    if (Error(OleRegisterClientDoc(szAppName, (LPSTR)pFileName, 0L, lhcptrDoc))) 
  811.    {
  812.       ErrorMessage(W_FAILED_TO_NOTIFY);
  813.       return FALSE;                    //* ERROR return
  814.    }
  815.    return TRUE;                        //* SUCCESS return
  816.  
  817. }    
  818.  
  819. /****************************************************************************
  820.  *  DeregDoc()
  821.  *
  822.  *  This function initiates the removal of all OLE objects from the
  823.  *  current document and deregisters the document with the OLE library.
  824.  ***************************************************************************/
  825.  
  826. static void DeregDoc(                  //* ENTRY:
  827.    LHCLIENTDOC    lhcDoc               //* client document handle
  828. ){
  829.  
  830.     if (lhcDoc) 
  831.     {                                  //* release all OLE objects
  832.         ClearAll(lhcDoc,RELEASE);      //* and remove them from the screen
  833.         WaitForAllObjects();
  834.         if (Error(OleRevokeClientDoc(lhcDoc)))
  835.             ErrorMessage(W_FAILED_TO_NOTIFY);
  836.     }
  837.  
  838. }                                      //* SUCCESS return
  839.  
  840. /***************************************************************************
  841.  *  ClearAll()
  842.  *
  843.  * This function will destroy all of the item windows in the current 
  844.  * document and delete all OLE objects.  The loop is basically an enum 
  845.  * of all child windows.
  846.  **************************************************************************/
  847.  
  848. static void ClearAll(                  //* ENTRY:
  849.    LHCLIENTDOC    lhcDoc,              //* application document handle
  850.    BOOL           fDelete              //* Delete / Release
  851. ){                                     //* LOCAL:
  852.    APPITEMPTR     pItemNext;           //* working handles
  853.    APPITEMPTR     pItem;               //* pointer to application item
  854.    
  855.    pItem = GetTopItem();
  856.  
  857.    while (pItem)     
  858.    {  
  859.       pItemNext = GetNextItem(pItem);
  860.       if (pItem->lhcDoc == lhcDoc)
  861.          ObjDelete(pItem, fDelete); 
  862.       pItem = pItemNext;
  863.    }
  864.  
  865. }  
  866.                                     //* SUCCESS return
  867. /***************************************************************************
  868.  * ClearItem()
  869.  *
  870.  * This function will destroy an item window, and make the 
  871.  * next window active.
  872.  **************************************************************************/
  873.  
  874. void  FAR ClearItem(                 //* ENTRY:
  875.    APPITEMPTR     pItem                //* application item pointer
  876. ){                                   
  877.  
  878.    pItem->fVisible = FALSE;
  879.    SetTopItem(GetNextActiveItem());
  880.    ObjDelete(pItem, DELETE);
  881.    Dirty(DOC_DIRTY);
  882.  
  883. }
  884.  
  885. /****************************************************************************
  886.  *  SaveAsNeeded()
  887.  *
  888.  *  This function will have the file saved if and only
  889.  *  if the document has been modified. If the fDirty flag has
  890.  *  been set to TRUE, then the document needs to be saved.
  891.  *
  892.  *  Returns: BOOL -  TRUE if document doesn't need saving or if the
  893.  *                   document has been saved successfully.
  894.  ***************************************************************************/
  895.  
  896. static BOOL SaveAsNeeded(              //* ENTRY:
  897.    PSTR           pFileName,           //* file to save
  898.    LHCLIENTDOC    lhcDoc,              //* OLE doc handle
  899.    LPAPPSTREAM    lpStream             //* pointer to OLE stream vtbl ... 
  900. ){                                     //* LOCAL:
  901.    char           sz[CBMESSAGEMAX];    //* work strings 
  902.    char           sz2[CBMESSAGEMAX + CBPATHMAX]; 
  903.  
  904.    if (Dirty(DOC_QUERY))               //* if doc is clean don't bother
  905.    {
  906.  
  907.       LoadString(hInst, IDS_MAYBESAVE, sz, CBMESSAGEMAX);
  908.       wsprintf(sz2, sz, (LPSTR)pFileName );
  909.  
  910.       switch (MessageBox(hwndFrame, sz2, szAppName, MB_YESNOCANCEL | MB_ICONQUESTION)) 
  911.       {
  912.  
  913.          case IDCANCEL:
  914.             return FALSE;              //* CANCEL return
  915.  
  916.          case IDYES:
  917.             return (SaveFile(pFileName,lhcDoc,lpStream));
  918.  
  919.          default:
  920.             break;
  921.       }
  922.    }
  923.    return TRUE;                        //* SUCCESS return
  924.  
  925. }
  926.  
  927. /****************************************************************************
  928.  *  SetTitle()
  929.  *
  930.  *  Set the window caption to the current file name. If szFileName is 
  931.  *  NULL, the caption will be set to "(Untitled)".
  932.  ***************************************************************************/
  933.  
  934. static void SetTitle(                  //* ENTRY:
  935.    PSTR           pFileName            //* file name
  936. ){                                     //* LOCAL
  937.                                        //* window title string
  938.    char           szTitle[CBMESSAGEMAX + CBPATHMAX];
  939.  
  940.    wsprintf(szTitle, "%s - %s", (LPSTR)szAppName, (LPSTR)pFileName);
  941.    SetWindowText(hwndFrame, szTitle);        
  942.  
  943. }
  944.  
  945. /***************************************************************************
  946.  *  EndClient()
  947.  *
  948.  *  Perform cleanup prior to app termination. The OLECLIENT
  949.  *  memory blocks and procedure instance thunks freed.
  950.  **************************************************************************/
  951.  
  952. static void EndStream(                 //* ENTRY:
  953.    LPAPPSTREAM    lpStream             //* pointer to stream structure
  954. ){                                     //* LOCAL:
  955.    HANDLE         hGeneric;            //* temp handle
  956.  
  957.     if (lpStream)                      //* is there a STREAM struct?  
  958.     {
  959.       if (lpStream->olestream.lpstbl)
  960.       {
  961.          FreeProcInstance((FARPROC)lpStream->olestream.lpstbl->Get);
  962.          FreeProcInstance((FARPROC)lpStream->olestream.lpstbl->Put);
  963.          hGeneric = (HANDLE)GlobalHandle(HIWORD(lpStream->olestream.lpstbl));
  964.          GlobalUnlock(hGeneric);
  965.          GlobalFree(hGeneric);
  966.       }
  967.       hGeneric = (HANDLE)GlobalHandle(HIWORD(lpStream));
  968.       GlobalUnlock(hGeneric);
  969.       GlobalFree(hGeneric);
  970.     }
  971.     if (lpfnTimerProc)
  972.         FreeProcInstance(lpfnTimerProc);
  973.  
  974. }                                      //* SUCCESS return
  975.  
  976. /***************************************************************************
  977.  *  EndClient()
  978.  *
  979.  *  Perform cleanup prior to app termination. The OLECLIENT
  980.  *  memory blocks and procedure instance thunks are freed.
  981.  **************************************************************************/
  982.  
  983. static void EndClient(                 //* ENTRY:
  984.    LPOLECLIENT    lpClient             //* pointer to client structure
  985. ){                                     //* LOCAL:
  986.    HANDLE         hGeneric;            //* temp handle
  987.  
  988.    if (lpClient)                       //* is there a client structure
  989.    {
  990.       if (lpClient->lpvtbl)
  991.       {
  992.          FreeProcInstance(lpClient->lpvtbl->CallBack);
  993.          hGeneric = (HANDLE)GlobalHandle(HIWORD(lpClient->lpvtbl));
  994.          GlobalUnlock(hGeneric);
  995.          GlobalFree(hGeneric);
  996.       }
  997.       hGeneric = (HANDLE)GlobalHandle(HIWORD(lpClient));
  998.       GlobalUnlock(hGeneric);
  999.       GlobalFree(hGeneric);
  1000.    }
  1001.  
  1002. }                                      //* SUCCESS return
  1003.  
  1004. /****************************************************************************
  1005.  * QueryEndSession()
  1006.  ***************************************************************************/
  1007.  
  1008. static long QueryEndSession(           //* ENTRY:
  1009.    PSTR           pFileName,           //* document name
  1010.    LHCLIENTDOC    lhcDoc,              //* client document handle
  1011.    LPAPPSTREAM    lpStream             //* application stream pointer
  1012. ){                                     //* LOCAL:
  1013.    APPITEMPTR     pItem;               //* application item pointer
  1014.       
  1015.  
  1016.    for (pItem = GetTopItem(); pItem; pItem = GetNextItem(pItem))
  1017.       if (OleQueryOpen(pItem->lpObject) == OLE_OK)
  1018.       {
  1019.          MessageBox(hwndFrame,"Exit CliDemo before closing Windows",
  1020.                szAppName, MB_OK | MB_ICONSTOP);
  1021.          return 0L;
  1022.       }
  1023.  
  1024.    if (!SaveAsNeeded(pFileName, lhcDoc, lpStream))
  1025.       return 0L;
  1026.    DeregDoc(lhcDoc);
  1027.    return 1L;
  1028.  
  1029. }
  1030.  
  1031.